#include "StdAfx.h"
#include "RendElement.h"
#include "CRECloud.h"
#include <I3DEngine.h>

uint32  CRECloud::m_siShadeResolution = 32;
float CRECloud::m_sfAlbedo = 0.9f;
float CRECloud::m_sfExtinction = 80.0f;
float CRECloud::m_sfTransparency = exp(-m_sfExtinction);
float CRECloud::m_sfScatterFactor = m_sfAlbedo * m_sfExtinction * (1.0f/(4.0f*(float)M_PI));
float CRECloud::m_sfSortAngleErrorTolerance = 0.8f;
float CRECloud::m_sfSortSquareDistanceTolerance = 100.0f;

float CRECloud::mfDistanceToCameraSquared(Matrix34& matInst)
{
  CRenderer *rd = gRenDev;

  Vec3 Center = m_boundingBox.GetCenter();
  Center += matInst.GetTranslation();
  Vec3 Delta = rd->GetRCamera().Orig - Center;
  return (Delta).GetLengthSquared();
}

void CRECloud::SortParticles(const Vec3& vViewDir, const Vec3& vSortPoint, ESortDirection eDir)
{
  Vec3 partPos;
  for (uint32 i=0; i<m_particles.size(); ++i)
  {
    partPos = m_particles[i]->GetPosition();
    partPos -= vSortPoint;
    m_particles[i]->SetSquareSortDistance(partPos * vViewDir);
  }

  switch (eDir)
  {
    case eSort_TOWARD:
      std::sort(m_particles.begin(), m_particles.end(), m_towardComparator);
      break;
    case eSort_AWAY:
      std::sort(m_particles.begin(), m_particles.end(), m_awayComparator);
      break;
    default:
      break;
  }
}

void CRECloud::GetIllumParams(ColorF& specColor, ColorF& diffColor)
{
  SRenderShaderResources *pRes = gRenDev->m_RP.m_pShaderResources;
  if (pRes && pRes->m_Constants[eHWSC_Pixel].size())
  {
    ColorF *pSrc = (ColorF *)&pRes->m_Constants[eHWSC_Pixel][0];
    specColor = pSrc[PS_SPECULAR_COL];
    diffColor = pSrc[PS_DIFFUSE_COL];
  }
  else
  {
    ColorF col = gRenDev->m_RP.m_pSunLight->m_Color;
    float fLum = col.Luminance();
    col.NormalizeCol(diffColor);
    specColor.a = 1.0f;
    specColor = specColor*fLum/1.5f;
    diffColor = gRenDev->m_RP.m_pCurObject->m_II.m_AmbColor / 5.0f;
  }
}

void CRECloud::ShadeCloud(Vec3 vPos)
{
  ColorF specColor, diffColor;
  if (gRenDev->m_RP.m_pSunLight)
  {
    GetIllumParams(specColor, diffColor);
    //IlluminateCloud(gRenDev->m_RP.m_pSunLight->m_Origin, vPos, difColor, ambColor, true);
    m_CurSpecColor = specColor;
    m_CurDiffColor = diffColor;
    m_bReshadeCloud = false;
    if (gRenDev->m_RP.m_pCurObject && gRenDev->m_RP.m_pCurObject->GetRE(gRenDev->m_RP.m_nProcessThreadID))
    {
      CREImposter *pRE = (CREImposter *)gRenDev->m_RP.m_pCurObject->GetRE(gRenDev->m_RP.m_nProcessThreadID);
      pRE->m_bScreenImposter = true;
    }
  }
}

void CRECloud::UpdateWorldSpaceBounds(CRenderObject *pObj)
{
  CREImposter *pRE = (CREImposter *)pObj->GetRE(gRenDev->m_RP.m_nProcessThreadID);
  assert(pRE);
  if (!pRE)
    return;
  pRE->m_WorldSpaceBV = m_boundingBox;
  if (m_Flags & FCEF_OLD)
  {
    Matrix34 mScale = Matrix34::CreateScale(Vec3(m_fScale, m_fScale, m_fScale));
    pRE->m_WorldSpaceBV.Transform(mScale);
  }
  pRE->m_WorldSpaceBV.Transform(pObj->m_II.m_Matrix);
}

void CRECloud::mfPrepare(bool bCheckOverflow)
{
  CRenderer *rd = gRenDev;

  if (bCheckOverflow)
    rd->FX_CheckOverflow(0, 0, this);

  CRenderObject *pObj = rd->m_RP.m_pCurObject;
  CREImposter *pRE = (CREImposter *)pObj->GetRE(rd->m_RP.m_nProcessThreadID);
  SRenderObjData *pOD = pObj->GetObjData(rd->m_RP.m_nProcessThreadID);
  assert(pOD);
  if (!pOD)
    return;
  if (!pRE)
  {
    pRE = new CREImposter;
    pOD->m_pRE = pRE;
    pRE->m_State = GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA | GS_ALPHATEST_GREATER;
    pRE->m_AlphaRef = 0;
  }

  if (pOD->m_fTempVars[0] != pOD->m_fTempVars[1])
  {
    pOD->m_fTempVars[1] = pOD->m_fTempVars[0];
    m_bReshadeCloud = true;
  }
  if (m_Flags & FCEF_OLD)
  {
    float fCloudRadius = m_boundingBox.GetRadius();
    m_fScale = pOD->m_fTempVars[0] / fCloudRadius;
  }
  else
	  m_fScale = pOD->m_fTempVars[0];

  ColorF specColor, diffColor;
  GetIllumParams(specColor, diffColor);
  if (specColor != m_CurSpecColor || diffColor != m_CurDiffColor)
    m_bReshadeCloud = true;

  UpdateWorldSpaceBounds(pObj);
  Vec3 vPos = pRE->GetPosition();




  {
    if (m_bReshadeCloud)
      ShadeCloud(vPos);
    UpdateImposter(pObj);
  }

  rd->m_RP.m_pCurObject = pObj;
  rd->m_RP.m_pRE = this;
  rd->m_RP.m_RendNumIndices = 0;
  rd->m_RP.m_RendNumVerts = 4;
  rd->m_RP.m_FirstVertex = 0;
}

bool CRECloud::mfLoadCloud(const string& name, float fScale, bool bLocal)
{
  uint32 i;
  FILE *fp = gEnv->pCryPak->FOpen(name.c_str(), "rb");
  if (!fp)
    return false;

  uint32 iNumParticles;

  mfSetFlags(FCEF_OLD);

  gEnv->pCryPak->FRead(&iNumParticles, 1, fp);
  if (iNumParticles == 0x238c)
  {
    char fTexName[128];
    char texName[256];
    gEnv->pCryPak->FRead(&iNumParticles, 1, fp);
    int n = 0;
    int ch;
    do
    {
      ch = gEnv->pCryPak->Getc(fp);
      fTexName[n++] = ch;
      if (n > 128)
      {
        fTexName[127] = ch;
        break;
      }
    } while (ch != 0);
    fpStripExtension(fTexName, fTexName);
    sprintf(texName, "Textures/Clouds/%s.dds", fTexName);
    m_pTexParticle = CTexture::ForName(texName, 0, eTF_Unknown);
    /*byte *pData = m_pTexParticle->GetData32(0, 0);
    int nWdt = m_pTexParticle->GetWidth();
    int nHgt = m_pTexParticle->GetHeight();
    int nSize = nWdt * nHgt;
    for (i=0; i<nSize; i++)
    {
      uint32 d = *(uint32 *)&pData[i*4];
      ColorF c = ColorF(d);
      c.r *= c.a;
      c.g *= c.a;
      c.b *= c.a;
      ColorB b = c;
      *(uint32 *)&pData[i*4] = b.pack_argb8888();
    }
    sprintf(texName, "Textures/Clouds/%s.dds", fTexName);
    int nMips = CTexture::CalcNumMips(nWdt, nHgt);
    int nOutSize;
    byte *pD = CTexture::Convert(pData, nWdt, nHgt, 1, eTF_A8R8G8B8, eTF_A8R8G8B8, nMips, nOutSize);
    ::WriteDDS(pD, nWdt, nHgt, texName, eTF_A8R8G8B8, nMips, eTT_2D);
    delete [] pD;
    delete [] pData;*/
    gEnv->pCryPak->FRead(&m_nNumColorGradients, 1, fp);
    //m_pColorGradients = new SColorLevel [m_nNumColorGradients];
    for (i=0; i<m_nNumColorGradients; i++)
    {
      float fLevel;
      gEnv->pCryPak->FRead(&fLevel, 1, fp);
      //m_pColorGradients[i].m_fLevel /= 100.0f;
      uint32 iColor;
      gEnv->pCryPak->FRead(&iColor, 1, fp);
      //m_pColorGradients[i].m_vColor = ColorF(iColor);
    }
    for (i=0; i<iNumParticles; ++i)
    {
      Vec3 vPosition;
      short nShadingNum;
      short nGroupNum;
      short nWidthMin, nWidthMax;
      short nLengthMin, nLengthMax;
      short nRotMin, nRotMax;
      Vec2 vUV[2];
      gEnv->pCryPak->FRead(&vPosition, 1, fp);
      gEnv->pCryPak->FRead(&nShadingNum, 1, fp);
      gEnv->pCryPak->FRead(&nGroupNum, 1, fp);
      gEnv->pCryPak->FRead(&nWidthMin, 1, fp);
      gEnv->pCryPak->FRead(&nWidthMax, 1, fp);
      gEnv->pCryPak->FRead(&nLengthMin, 1, fp);
      gEnv->pCryPak->FRead(&nLengthMax, 1, fp);
      gEnv->pCryPak->FRead(&nRotMin, 1, fp);
      gEnv->pCryPak->FRead(&nRotMax, 1, fp);
      gEnv->pCryPak->FRead(&vUV[0], 1, fp);
      gEnv->pCryPak->FRead(&vUV[1], 1, fp);

      vPosition *= 0.001f;
      float fWidth = (float)nWidthMin * 0.001f;
      float fHeight = (float)nLengthMin * 0.001f;
      float fRotMin = (float)nRotMin;
      float fRotMax = (float)nRotMax;
      Exchange(vPosition.y, vPosition.z);
      vUV[0].y = 1.0f - vUV[0].y;
      vUV[1].y = 1.0f - vUV[1].y;
      Exchange(vUV[0].x, vUV[1].x);
      /*int nX = nShadingNum & 0x3;
      int nY = nShadingNum >> 2;
      vUV[0].x = (float)nX * 0.25f;
      vUV[1].x = vUV[0].x + 0.25f;
      vUV[0].y = (float)nY * 0.25f;
      vUV[1].y = vUV[0].y + 0.25f;*/
      SCloudParticle *pParticle = new SCloudParticle(vPosition, fWidth, fHeight, fRotMin, fRotMax, vUV);

      Vec3 vMin = pParticle->GetPosition() - Vec3(fWidth, fWidth, fHeight);
      Vec3 vMax = pParticle->GetPosition() + Vec3(fWidth, fWidth, fHeight);
      m_boundingBox.AddPoint(vMin);
      m_boundingBox.AddPoint(vMax);
      m_particles.push_back(pParticle);
    }
    Vec3 vCenter = m_boundingBox.GetCenter();
    if (vCenter != Vec3(0,0,0))
    {
      m_boundingBox.Clear();
      for (i=0; i<iNumParticles; i++)
      {
        SCloudParticle *pParticle = m_particles[i];
        pParticle->SetPosition(pParticle->GetPosition() - vCenter);
        float fWidth = pParticle->GetRadiusX();
        float fHeight = pParticle->GetRadiusY();
        Vec3 vMin = pParticle->GetPosition() - Vec3(fWidth, fWidth, fHeight);
        Vec3 vMax = pParticle->GetPosition() + Vec3(fWidth, fWidth, fHeight);
        m_boundingBox.AddPoint(vMin);
        m_boundingBox.AddPoint(vMax);
      }
    }
  }
  else
  {
		Vec3 vCenter = Vec3(0,0,0);
    gEnv->pCryPak->FRead(&vCenter[0], 1, fp);
    vCenter = Vec3(0,0,0);

    Vec3 *pParticlePositions = new Vec3[iNumParticles];
    float *pParticleRadii     = new float[iNumParticles];
    ColorF *pParticleColors    = new ColorF[iNumParticles];

    gEnv->pCryPak->FRead(pParticlePositions, iNumParticles, fp);
    gEnv->pCryPak->FRead(pParticleRadii, iNumParticles, fp);
    gEnv->pCryPak->FRead(pParticleColors, iNumParticles, fp);

    for (i=0; i<iNumParticles; ++i)
    {
      if (pParticleRadii[i] < 0.8f)
        continue;
      pParticleRadii[i] *= 1.25f;
      Exchange(pParticlePositions[i].y, pParticlePositions[i].z);
      SCloudParticle *pParticle = new SCloudParticle((pParticlePositions[i]+vCenter)*fScale, pParticleRadii[i]*fScale, pParticleColors[i]);

      float fRadiusX = pParticle->GetRadiusX();
      float fRadiusY = pParticle->GetRadiusX();
      Vec3 vRadius = Vec3(fRadiusX, fRadiusX, fRadiusY);

      //Vec3 Mins = pParticle->GetPosition() - vRadius;
      //Vec3 Maxs = pParticle->GetPosition() + vRadius;
      //m_boundingBox.AddPoint(Mins);
      //m_boundingBox.AddPoint(Maxs);
      m_boundingBox.AddPoint(pParticle->GetPosition());

      m_particles.push_back(pParticle);
    }
    SAFE_DELETE_ARRAY(pParticleColors);
    SAFE_DELETE_ARRAY(pParticleRadii);
    SAFE_DELETE_ARRAY(pParticlePositions);

    m_pTexParticle = CTexture::s_ptexWhite;
  }

  gEnv->pCryPak->FClose(fp);

  return true;
}

bool CRECloud::mfCompile(CParserBin& Parser, SParserFrame& Frame)
{
  SParserFrame OldFrame = Parser.BeginFrame(Frame);

  FX_BEGIN_TOKENS
    FX_TOKEN(ParticlesFile)
    FX_TOKEN(Scale)
  FX_END_TOKENS

  bool bRes = true;
  float fScale = 1.0f;
  string pname;
  ColorF col;

  while (Parser.ParseObject(sCommands))
  {
    EToken eT = Parser.GetToken();
    switch (eT)
    {
    case eT_ParticlesFile:
      pname = Parser.GetString(Parser.m_Data);
      break;
    case eT_Scale:
      fScale = Parser.GetFloat(Parser.m_Data);
      break;
    }
  }

  if (!pname.empty())
    mfLoadCloud(pname, fScale, false);
  m_bReshadeCloud = true;

  Parser.EndFrame(OldFrame);

  return bRes;
}

#ifndef _LIB
	#include "Common_TypeInfo.h"
#endif

#include UNIQUE_VIRTUAL_WRAPPER(CREBaseCloud)